package com.nuc.seckill.utils;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.sql.*;
import java.util.*;

/**
 * 底层代码生成器
 *
 * @author 石一歌
 * @date 2022-07-07 22:02
 */
public class EntityUtil {
    private static final String PROJECT_NAME = "seckill";

    private static final String MODULE_NAME = "seckill";

    private static final String USER = "root";

    private static final String PASSWORD = "root";

    private static final String BASE_FILE_PATH = System.getProperty("user.dir") + "\\seckill-service";

    private static final String TYPE_CHAR = "char";

    private static final String TYPE_DATE = "date";

    private static final String TYPE_TIMESTAMP = "timestamp";

    private static final String TYPE_INT = "int";

    private static final String TYPE_BIGINT = "bigint";

    private static final String TYPE_TEXT = "text";

    private static final String TYPE_BIT = "bit";

    private static final String TYPE_DECIMAL = "decimal";

    private static final String TYPE_BLOB = "blob";

    private static final String BEAN_PATH = BASE_FILE_PATH + "\\src\\main\\java\\";

    private static final String MAPPER_PATH = BASE_FILE_PATH + "\\src\\main\\java\\com\\nuc\\" + PROJECT_NAME + "\\dao";

    private static final String XML_PATH = BASE_FILE_PATH + "\\src\\main\\resources\\mapping";

    private static final String MODEL_PACKAGE = "com.nuc." + PROJECT_NAME + ".model";

    private static final String DAO_PACKAGE = "com.nuc." + PROJECT_NAME + ".dao";

    private static final String UTILS_PACKAGE = "com.nuc." + PROJECT_NAME + ".utils";

    private static final String DRIVER_NAME = "com.mysql.cj.jdbc.Driver";

    private static final String URL = "jdbc:mysql://127.0.0.1:3306/" + MODULE_NAME
            + "?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2b8";

    private String tableName = null;

    private String beanName = null;

    private String mapperName = null;

    private Connection conn = null;


    public static String createPackagePath(String packageName) {
        StringBuilder sbBuffer = new StringBuilder();
        String[] arrays = packageName.split("\\.");
        for (String str : arrays) {
            sbBuffer.append(str).append(File.separator);
        }
        return sbBuffer.toString();
    }

    public static void main(String[] args) {
        try {
            new EntityUtil().generate();
        } catch (ClassNotFoundException | SQLException | IOException e) {
            e.printStackTrace();
        }
    }

    private void init() throws ClassNotFoundException, SQLException {
        Class.forName(DRIVER_NAME);
        conn = DriverManager.getConnection(URL, USER, PASSWORD);
    }

    /**
     * 获取所有的表
     *
     * @return java.util.List<java.lang.String>
     * @author 石一歌
     * @date 2022/7/7 22:50
     */
    private List<String> getTables() throws SQLException {
        List<String> tables = new ArrayList<>();
        PreparedStatement pState = conn.prepareStatement("show tables");
        ResultSet results = pState.executeQuery();
        while (results.next()) {
            String tableName = results.getString(1);
            tables.add(tableName);
        }
        return tables;
    }

    private void processTable(String table) {
        StringBuilder sb = new StringBuilder(table.length());
        String tableNew = table.toLowerCase();
        String[] tables = tableNew.split("_");
        String temp;
        for (String s : tables) {
            temp = s.trim();
            sb.append(temp.substring(0, 1).toUpperCase()).append(temp.substring(1));
        }
        beanName = sb.toString();
        mapperName = beanName;
    }

    private Set<String> processImportType(List<String> columns, List<String> types) {
        Set<String> set = new HashSet<>();
        for (int i = 0; i < columns.size(); i++) {
            String type = types.get(i);
            if (type.contains(TYPE_DATE)) {
                set.add("import java.util.Date;");
            } else if (type.contains(TYPE_TIMESTAMP)) {
                set.add("import java.util.Date;");
            } else if (type.contains(TYPE_DECIMAL)) {
                set.add("import java.math.BigDecimal;");
            }
        }
        return set;
    }

    private String processType(String type) {
        if (type.contains(TYPE_CHAR)) {
            return "String";
        } else if (type.contains(TYPE_BIGINT)) {
            return "Long";
        } else if (type.contains(TYPE_INT)) {
            return "Integer";
        } else if (type.contains(TYPE_DATE)) {
            return "Date";
        } else if (type.contains(TYPE_TEXT)) {
            return "String";
        } else if (type.contains(TYPE_TIMESTAMP)) {
            return "Date";
        } else if (type.contains(TYPE_BIT)) {
            return "Boolean";
        } else if (type.contains(TYPE_DECIMAL)) {
            return "BigDecimal";
        } else if (type.contains(TYPE_BLOB)) {
            return "byte[]";
        }
        return null;
    }

    private boolean processTypeIfCase(String type) {
        if (type.contains(TYPE_CHAR)) {
            return true;
        } else {
            return type.contains(TYPE_TEXT);
        }
    }

    private String processField(String field) {
        String s = "__";
        System.out.println(field + "!!!!!!!");
        StringBuilder sb = new StringBuilder(field.length());
        if (field.contains(s)) {
            return field;
        }
        String[] fields = field.split("_");
        String temp;
        sb.append(fields[0]);
        for (int i = 1; i < fields.length; i++) {
            temp = fields[i].trim();
            sb.append(temp.substring(0, 1).toUpperCase()).append(temp.substring(1));
        }
        return sb.toString();
    }

    /**
     * 构建类上面的注释
     *
     * @param bw   輸出
     * @param text 内容
     * @author 石一歌
     * @date 2022/7/7 23:00
     */
    private void buildClassComment(BufferedWriter bw, String text) throws IOException {
        bw.newLine();
        bw.newLine();
        bw.write("/**");
        bw.newLine();
        bw.write(" * ");
        bw.newLine();
        bw.write(" * " + text);
        bw.newLine();
        bw.write(" * ");
        bw.newLine();
        bw.write(" **/");
    }

    /**
     * 构建方法上面的注释
     *
     * @param bw   输出
     * @param text 内容
     * @author 石一歌
     * @date 2022/7/7 23:02
     */
    private void buildMethodComment(BufferedWriter bw, String text) throws IOException {
        bw.newLine();
        bw.write("\t/**");
        bw.newLine();
        bw.write("\t * ");
        bw.newLine();
        bw.write("\t * " + text);
        bw.newLine();
        bw.write("\t * ");
        bw.newLine();
        bw.write("\t **/");
    }

    /**
     * 生成实体类
     *
     * @param columns      列
     * @param types        类型
     * @param comments     注释
     * @param tableComment 表注释
     * @author 石一歌
     * @date 2022/7/7 23:02
     */
    private void buildEntityBean(List<String> columns, List<String> types, List<String> comments,
                                 String tableComment) throws IOException {
        // 创建包目录
        String packagePath = BEAN_PATH + File.separator + createPackagePath(MODEL_PACKAGE);
        File folder = new File(packagePath);
        if (!folder.exists()) {
            System.out.println(folder.mkdirs());
        }

        File beanFile = new File(packagePath, beanName + ".java");
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
                new FileOutputStream(beanFile)));
        bw.write("package " + MODEL_PACKAGE + ";");
        bw.newLine();
        bw.newLine();
        Set<String> set = processImportType(columns, types);
        for (String str : set) {
            bw.write(str);
            bw.newLine();
        }
        buildClassComment(bw, tableComment);
        bw.newLine();


        bw.write("public class " + beanName + "{");
        bw.newLine();
        bw.newLine();
        int size = columns.size();
        for (int i = 0; i < size; i++) {
            bw.write("\n  /**" + comments.get(i) + "**/");
            bw.newLine();
            bw.write("\n  private " + processType(types.get(i)) + " "
                    + processField(columns.get(i)) + ";");
            bw.newLine();
            bw.newLine();
        }
        bw.newLine();
        // 生成get 和 set方法
        String tempField;
        String tempFieldTwo;
        String tempType;
        for (int i = 0; i < size; i++) {
            tempType = processType(types.get(i));
            tempFieldTwo = processField(columns.get(i));
            tempField = tempFieldTwo.substring(0, 1).toUpperCase() + tempFieldTwo.substring(1);
            bw.newLine();
            bw.write("\n  public void set" + tempField + "(" + tempType + " " + tempFieldTwo + ") { ");
            bw.write("\r    this." + tempFieldTwo + " = " + tempFieldTwo + ";");
            bw.write("\n  }");
            bw.newLine();
            bw.newLine();
            bw.write("\n  public " + tempType + " get" + tempField + "() { ");
            bw.write("\r    return this." + tempFieldTwo + ";");
            bw.write("\n  }");
            bw.newLine();
        }
        bw.newLine();
        bw.write("}");
        bw.newLine();
        bw.flush();
        bw.close();
    }

    /**
     * 构建Mapper文件
     *
     * @param columns 列
     * @param types   类型
     * @author 石一歌
     * @date 2022/7/7 22:34
     */
    private void buildMapper(List<String> columns, List<String> types) throws IOException {
        File folder = new File(MAPPER_PATH);
        if (!folder.exists()) {
            System.out.println(folder.mkdirs());
        }

        File mapperFile = new File(MAPPER_PATH, mapperName + "Dao.java");
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(
                mapperFile), StandardCharsets.UTF_8));
        bw.write("package " + DAO_PACKAGE + ";");
        bw.newLine();
        bw.newLine();
        bw.write("import java.util.List;");
        bw.newLine();
        bw.write("import " + MODEL_PACKAGE + "." + beanName + ";");
        bw.newLine();

        bw.write("import " + UTILS_PACKAGE + ".bean.CommonQueryBean;");
        bw.newLine();
        bw.newLine();
        bw.write("import org.apache.ibatis.annotations.Param;");
        bw.newLine();
        bw.write("import org.springframework.stereotype.Repository;");

        buildClassComment(bw, mapperName + "数据库操作接口类");
        bw.newLine();
        bw.newLine();
        bw.write("@Repository");
        bw.newLine();
        bw.write("public interface " + mapperName + "Dao" + "{");
        bw.newLine();
        bw.newLine();
        // ----------定义Mapper中的方法Begin----------
        buildMethodComment(bw, "查询（根据主键ID查询）");
        bw.newLine();
        bw.write("\t" + beanName + "  selectByPrimaryKey ( @Param(\""
                + processField(columns.get(0)) + "\") " + processType(types.get(0)) + " "
                + processField(columns.get(0)) + " );");
        bw.newLine();
        buildMethodComment(bw, "删除（根据主键ID删除）");
        bw.newLine();
        bw.write("\t" + "int deleteByPrimaryKey ( @Param(\"" + processField(columns.get(0))
                + "\") " + processType(types.get(0)) + " " + processField(columns.get(0)) + " );");
        bw.newLine();
        buildMethodComment(bw, "添加");
        bw.newLine();
        bw.write("\t" + "int insert( " + beanName + " record );");
        bw.newLine();
        buildMethodComment(bw, "修改 （匹配有值的字段）");
        bw.newLine();
        bw.write("\t" + "int updateByPrimaryKeySelective( " + beanName + " record );");
        bw.newLine();
        buildMethodComment(bw, "list分页查询");
        bw.newLine();
        bw.write("\t" + "List<" + beanName + "> list4Page ( " + beanName
                + " record, @Param(\"commonQueryParam\") CommonQueryBean query);");
        bw.newLine();

        buildMethodComment(bw, "count查询");
        bw.newLine();
        bw.write("\t" + "long count ( " + beanName + " record);");
        bw.newLine();

        buildMethodComment(bw, "list查询");
        bw.newLine();
        bw.write("\t" + "List<" + beanName + "> list ( " + beanName + " record);");
        bw.newLine();

        // ----------定义Mapper中的方法End----------
        bw.newLine();
        bw.write("}");
        bw.flush();
        bw.close();
    }

    /**
     * 构建实体类映射XML文件
     *
     * @param columns 列
     * @param types   类型
     * @author 石一歌
     * @date 2022/7/7 22:32
     */
    private void buildMapperXml(List<String> columns, List<String> types)
            throws IOException {
        File folder = new File(XML_PATH);
        if (!folder.exists()) {
            System.out.println(folder.mkdirs());
        }

        File mapperXmlFile = new File(XML_PATH, mapperName + ".xml");
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(
                mapperXmlFile)));
        bw.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
        bw.newLine();
        bw.write("<!DOCTYPE mapper PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\" ");
        bw.newLine();
        bw.write("    \"http://mybatis.org/dtd/mybatis-3-mapper.dtd\">");
        bw.newLine();
        bw.write("<mapper namespace=\"" + DAO_PACKAGE + "." + mapperName + "Dao\">");
        bw.newLine();
        bw.newLine();

        buildMysql(bw, columns, types);

        bw.write("</mapper>");
        bw.flush();
        bw.close();
    }

    private void buildMysql(BufferedWriter bw, List<String> columns, List<String> types)
            throws IOException {
        bw.write("\t<resultMap id=\"" + beanName + "\" type=\"" + MODEL_PACKAGE + "." + beanName
                + "\" >");
        bw.newLine();
        for (int i = 1; i < columns.size(); i++) {
            bw.write("\t\t" + "<result column=\"" + columns.get(i) + "\" property=\""
                    + processField(columns.get(i)) + "\"/>");
            bw.newLine();
        }
        bw.write("\t</resultMap>");
        bw.newLine();
        bw.newLine();
        int size = columns.size();
        // 通用结果列
        bw.write("\t<!-- 通用查询结果列-->");
        bw.newLine();
        bw.write("\t<sql id=\"Base_Column_List\">");
        bw.newLine();
        for (int i = 0; i < size; i++) {
            bw.write("\t\t" + columns.get(i));
            if (i != size - 1) {
                bw.write(",");
                bw.newLine();
            }
        }
        bw.newLine();
        bw.write("\t</sql>");
        bw.newLine();
        bw.newLine();
        bw.write("\t<!-- 查询（根据主键ID查询） -->");
        bw.newLine();
        bw.write("\t<select id=\"selectByPrimaryKey\" resultMap=\"" + beanName
                + "\" parameterType=\"java.lang." + processType(types.get(0)) + "\">");
        bw.newLine();
        bw.write("\t\t SELECT");
        bw.newLine();
        bw.write("\t\t <include refid=\"Base_Column_List\" />");
        bw.newLine();
        bw.write("\t\t FROM " + tableName);
        bw.newLine();
        bw.write("\t\t WHERE " + columns.get(0) + " = #{" + processField(columns.get(0)) + "}");
        bw.newLine();
        bw.write("\t</select>");
        bw.newLine();
        bw.newLine();
        bw.write("\t<!--删除：根据主键ID删除-->");
        bw.newLine();
        bw.write("\t<delete id=\"deleteByPrimaryKey\" parameterType=\"java.lang."
                + processType(types.get(0)) + "\">");
        bw.newLine();
        bw.write("\t\t DELETE FROM " + tableName);
        bw.newLine();
        bw.write("\t\t WHERE " + columns.get(0) + " = #{" + processField(columns.get(0)) + "}");
        bw.newLine();
        bw.write("\t</delete>");
        bw.newLine();
        bw.newLine();
        bw.write("\t<!-- 添加 -->");
        bw.newLine();
        bw.write("\t<insert id=\"insert\" parameterType=\"" + MODEL_PACKAGE + "." + beanName
                + "\" >");
        bw.newLine();
        bw.write("\t\t INSERT INTO " + tableName);
        bw.newLine();
        bw.write(" \t\t(");
        bw.newLine();
        for (int i = 0; i < size; i++) {
            bw.write("\t\t\t " + columns.get(i));
            if (i != size - 1) {
                bw.write(",");
            }
            bw.newLine();
        }
        bw.write("\t\t) ");
        bw.newLine();
        bw.write("\t\t VALUES ");
        bw.newLine();
        bw.write(" \t\t(");
        bw.newLine();
        for (int i = 0; i < size; i++) {
            bw.write("\t\t\t " + "#{" + processField(columns.get(i)) + "}");
            if (i != size - 1) {
                bw.write(",");
            }
            bw.newLine();
        }
        bw.write(" \t\t) ");
        bw.newLine();
        bw.write("\t\t <selectKey keyProperty=\"" + processField(columns.get(0))
                + "\" resultType=\"" + processType(types.get(0)) + "\" order=\"AFTER\">");
        bw.newLine();
        bw.write("\t\t\t select LAST_INSERT_ID()");
        bw.newLine();
        bw.write("\t\t </selectKey>");
        bw.newLine();
        bw.write("\t</insert>");
        bw.newLine();
        bw.newLine();
        String tempField;
        bw.write("\t<!-- 修 改-->");
        bw.newLine();
        bw.write("\t<update id=\"updateByPrimaryKeySelective\" parameterType=\"" + MODEL_PACKAGE
                + "." + beanName + "\" >");
        bw.newLine();
        bw.write("\t\t UPDATE " + tableName);
        bw.newLine();
        bw.write(" \t\t <set> ");
        bw.newLine();
        for (int i = 1; i < size; i++) {
            tempField = processField(columns.get(i));
            if (processTypeIfCase(types.get(i))) {
                bw.write("\t\t\t<if test=\"" + tempField + " != null and " + tempField
                        + " != ''\">");
            } else {
                bw.write("\t\t\t<if test=\"" + tempField + " != null\">");
            }
            bw.newLine();
            bw.write("\t\t\t\t " + columns.get(i) + " = #{" + tempField + "},");
            bw.newLine();
            bw.write("\t\t\t</if>");
            bw.newLine();
        }
        bw.newLine();
        bw.write(" \t\t </set>");
        bw.newLine();
        bw.write("\t\t WHERE " + columns.get(0) + " = #{" + processField(columns.get(0)) + "}");
        bw.newLine();
        bw.write("\t</update>");
        bw.newLine();
        bw.newLine();
        bw.write("\t<!-- list4Page 分页查询-->");
        bw.newLine();
        bw.write("\t<select id=\"list4Page\" resultMap=\"" + beanName + "\">");
        newLine(bw);
        for (int i = 0; i < size; i++) {
            tempField = processField(columns.get(i));
            if (processTypeIfCase(types.get(i))) {
                bw.write("\t\t<if test=\"record." + tempField + " != null and record." + tempField
                        + " != ''\">");
            } else {
                bw.write("\t\t<if test=\"record." + tempField + " != null\">");
            }
            bw.newLine();
            bw.write("\t\t\t and " + columns.get(i) + " = #{record." + tempField + "} ");
            bw.newLine();
            bw.write("\t\t</if>");
            bw.newLine();
        }
        bw.write("\t\t<if test=\"" + "commonQueryParam" + " != null\">");
        bw.newLine();
        bw.write("\t\t\t<if test=\"commonQueryParam.order != null \">");
        bw.newLine();
        bw.write("\t\t\t\t order by #{commonQueryParam.order}");
        bw.write("\t\t\t<if test=\"commonQueryParam.sort != null \">");
        bw.write("\t\t\t\t #{commonQueryParam.sort}");
        bw.write("\t\t\t</if>");
        bw.newLine();
        bw.write("\t\t\t</if>");
        bw.newLine();
        bw.write("\t\t\t<if test=\"commonQueryParam.start != null  and commonQueryParam.pageSize != null\">");
        bw.newLine();
        bw.write("\t\t\t\t limit #{commonQueryParam.start}, #{commonQueryParam.pageSize}");
        bw.newLine();
        bw.write("\t\t\t</if>");
        bw.newLine();
        bw.write("\t\t</if>");
        bw.newLine();
        bw.write("\t</select>");
        bw.newLine();
        bw.write("\t<!-- count 总数-->");
        bw.newLine();
        bw.write("\t<select id=\"count\" resultType=\"long\">");
        bw.newLine();
        bw.write("\t\t SELECT ");
        bw.newLine();
        bw.write("\t\t count(1) ");
        bw.newLine();
        bw.write("\t\t from " + tableName);
        bw.newLine();
        bw.write(" \t\t where 1=1  ");
        bw.newLine();
        forProcessField(bw, columns, types, size);
        bw.write("\t<!-- list 查询-->");
        bw.newLine();
        bw.write("\t<select id=\"list\" resultMap=\"" + beanName + "\">");
        newLine(bw);
        forProcessField(bw, columns, types, size);
    }

    private void forProcessField(BufferedWriter bw, List<String> columns, List<String> types, int size) throws IOException {
        String tempField;
        for (int i = 0; i < size; i++) {
            tempField = processField(columns.get(i));
            if (processTypeIfCase(types.get(i))) {
                bw.write("\t\t<if test=\"" + tempField + " != null and " + tempField + " != ''\">");
            } else {
                bw.write("\t\t<if test=\"" + tempField + " != null\">");
            }
            bw.newLine();
            bw.write("\t\t\t and " + columns.get(i) + " = #{" + tempField + "} ");
            bw.newLine();
            bw.write("\t\t</if>");
            bw.newLine();
        }
        bw.write("\t</select>");
        bw.newLine();
    }

    private void newLine(BufferedWriter bw) throws IOException {
        bw.newLine();
        bw.write("\t\t SELECT ");
        bw.newLine();
        bw.write("\t\t <include refid=\"Base_Column_List\" />");
        bw.newLine();
        bw.write("\t\t from " + tableName);
        bw.newLine();
        bw.write(" \t\t where 1=1  ");
        bw.newLine();
    }

    /**
     * 获取所有的数据库表注释
     *
     * @return java.util.Map<java.lang.String, java.lang.String>
     * @author 石一歌
     * @date 2022/7/7 22:30
     */
    private Map<String, String> getTableComment() throws SQLException {
        Map<String, String> maps = new HashMap<>(16);
        PreparedStatement pState = conn.prepareStatement("show table status");
        ResultSet results = pState.executeQuery();
        while (results.next()) {
            String tableName = results.getString("NAME");
            String comment = results.getString("COMMENT");
            maps.put(tableName, comment);
        }
        return maps;
    }

    public void generate() throws ClassNotFoundException, SQLException, IOException {
        init();
        String prefix = "show full fields from ";
        List<String> columns;
        List<String> types;
        List<String> comments;
        PreparedStatement pState;
        List<String> tables = getTables();
        Map<String, String> tableComments = getTableComment();
        for (String table : tables) {
            columns = new ArrayList<>();
            types = new ArrayList<>();
            comments = new ArrayList<>();
            pState = conn.prepareStatement(prefix + table);
            ResultSet results = pState.executeQuery();
            while (results.next()) {
                columns.add(results.getString("FIELD").toLowerCase());
                types.add(results.getString("TYPE"));
                comments.add(results.getString("COMMENT"));
            }
            tableName = table;
            processTable(table);
            String tableComment = tableComments.get(tableName);
            buildEntityBean(columns, types, comments, tableComment);
            buildMapper(columns, types);
            buildMapperXml(columns, types);
        }
        conn.close();
    }
}
